package io.jpress.addon.oracle;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;

import javax.sql.DataSource;

import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.Db;

import io.jboot.aop.JbootAopFactory;
import io.jboot.db.JbootDbManager;
import io.jboot.db.datasource.DataSourceBuilder;
import io.jboot.db.datasource.DataSourceConfig;
import io.jboot.db.datasource.DataSourceConfigManager;
import io.jboot.utils.FileUtil;
import io.jboot.utils.StrUtil;
import io.jpress.JPressOptions;
import io.jpress.commons.utils.CommonsUtils;
import io.jpress.core.addon.AddonInfo;
import io.jpress.core.addon.AddonUtil;

public class AddonRecordPluginUtil {
	private static final Log LOG = Log.getLog(AddonRecordPluginUtil.class);

	public static boolean start(AddonInfo addonInfo) {
		if(addonInfo.getArp()!=null) {
			stop(addonInfo);
		}
		if(addonInfo.getArp()==null) {
			try {
				ActiveRecordPlugin arp = AddonRecordPluginUtil.createRecordPlugin(addonInfo);
				if (arp!=null && arp.start()) {
					addonInfo.setArp(arp);
					LOG.info(addonInfo.getId() + " db pool started");
				}
			}catch(Exception e) {
				
			}
		}
		return true;
	}
	
	public static void closeDataSource(DataSource ds) {
		Method method;
		try {
			method = ds.getClass().getDeclaredMethod("close");
			method.invoke(ds);
		} catch (Throwable e) {
			LOG.warn(e.getMessage());
		} 
	}

	public static String getFileText(AddonInfo addonInfo,String name) {
		File file = AddonUtil.resourceFile(addonInfo.getId(), name);
		if (!file.exists()) {
			LOG.warn("file not exists : " + file);
			return null;
		}
		String sql = FileUtil.readString(file);
		if (StrUtil.isBlank(sql)) {
			LOG.warn("can not read sql in : " + file);
			return null;
		}
		return sql;
	}
	
	public static boolean initDB(AddonInfo addonInfo) {
		try {
			String sql=getFileText(addonInfo,"db.sql");
			execSql(addonInfo, sql);
			return true;
		}catch(Throwable e) {
			LOG.warn("initDB",e);
		}
		return false;
	}
	
	public static boolean testLink(String config) throws Exception {
		DataSource dataSource = null;
        Connection conn = null;
        Statement pst = null;
        try {
            DataSourceConfig dataSourceConfig = getDatasourceConfig(config);
            dataSource = new DataSourceBuilder(dataSourceConfig).build();
        	conn = dataSource.getConnection();
        	if(conn!=null)
        		return true;
        }
        finally {
            CommonsUtils.quietlyClose(pst, conn);
            closeDataSource(dataSource);
        }
		return true;
	}
	
    public static void execSql(AddonInfo addonInfo, String sql) throws SQLException {
        DataSourceConfig dataSourceConfig = getDatasourceConfig(addonInfo);
        DataSource dataSource = new DataSourceBuilder(dataSourceConfig).build();

        Connection conn = dataSource.getConnection();
        Statement pst = null;
        try {
            pst = conn.createStatement();
            sql = StrUtil.requireNonBlank(sql, "sql must not be null or blank.");
            if (sql.contains(";")) {
                String sqls[] = sql.split(";");
                for (String s : sqls) {
                    if (StrUtil.isNotBlank(s)) {
                        pst.addBatch(s);
                    }
                }
            } else {
                pst.addBatch(sql);
            }
            pst.executeBatch();
        } finally {
            CommonsUtils.quietlyClose(pst, conn);
            closeDataSource(dataSource);
        }
    }

	public static void stop(AddonInfo addonInfo) {
        ActiveRecordPlugin arp = addonInfo.getArp();
        if (arp != null) {
            arp.stop();
            DataSource ds=arp.getConfig().getDataSource();
            closeDataSource(ds);
            addonInfo.setArp(null);
            LOG.info(addonInfo.getId() + " db pool closed");
        }
	}

	public static ActiveRecordPlugin createRecordPlugin(AddonInfo addonInfo) {
		DataSourceConfig config = getDatasourceConfig(addonInfo);
		if(config!=null) {
			config.setName(addonInfo.getId());
			config.setNeedAddMapping(false);
			return JbootDbManager.me().createRecordPlugin(config);
		}else
			return null;
	}
	
	public static DataSourceConfig getDatasourceConfig(AddonInfo addonInfo) {
		return getDatasourceConfig(JPressOptions.get(addonInfo.getId()+".db.config"));
	}
	
	public static DataSourceConfig getDatasourceConfig(String str) {
		Map<String, String> config = new HashMap<String, String>();
		Properties prop = new Properties();
		try {
			prop.load(new StringReader(str));
			for (Entry<Object, Object> entry : prop.entrySet()) {
				config.put(entry.getKey().toString(), entry.getValue().toString());
			}
		} catch (Throwable e) {
		}

		if (config == null || config.isEmpty()) {
			return null;
		}

		String url = config.get("db.url");
		String user = config.get("db.user");

		/**
		 * must need url and user
		 */
		if (config == null || StrUtil.isBlank(url) || StrUtil.isBlank(user)) {
			return null;
		}

		DataSourceConfig dsc = new DataSourceConfig();
		dsc.setUrl(url);
		dsc.setUser(user);
		dsc.setPassword(config.get("db.password"));
		dsc.setConnectionInitSql(config.get("db.connectionInitSql"));
		dsc.setConnectionTestQuery(config.get("db.connectionTestQuery"));
		dsc.setPoolName(config.get("db.poolName"));
		dsc.setSqlTemplate(config.get("db.sqlTemplate"));
		dsc.setSqlTemplatePath(config.get("db.sqlTemplatePath"));
		dsc.setFactory(config.get("db.factory"));
		dsc.setShardingConfigYaml(config.get("db.shardingConfigYaml"));
		dsc.setDbProFactory(config.get("db.dbProFactory"));
		dsc.setTable(config.get("db.table"));
		dsc.setExTable(config.get("db.exTable"));
		dsc.setDialectClass(config.get("db.dialectClass"));
		dsc.setActiveRecordPluginClass(config.get("db.activeRecordPluginClass"));
		dsc.setContainerFactory(config.get("db.containerFactory"));

		String cachePrepStmts = config.get("db.cachePrepStmts");
		String prepStmtCacheSize = config.get("db.prepStmtCacheSize");
		String prepStmtCacheSqlLimit = config.get("db.prepStmtCacheSqlLimit");
		String maximumPoolSize = config.get("db.maximumPoolSize");
		String maxLifetime = config.get("db.maxLifetime");
		String minimumIdle = config.get("db.minimumIdle");

		if (StrUtil.isNotBlank(cachePrepStmts)) {
			dsc.setCachePrepStmts(Boolean.valueOf(cachePrepStmts));
		}

		if (StrUtil.isNotBlank(prepStmtCacheSize)) {
			dsc.setPrepStmtCacheSize(Integer.valueOf(prepStmtCacheSize));
		}

		if (StrUtil.isNotBlank(prepStmtCacheSqlLimit)) {
			dsc.setPrepStmtCacheSqlLimit(Integer.valueOf(prepStmtCacheSqlLimit));
		}

		if (StrUtil.isNotBlank(maximumPoolSize)) {
			dsc.setMaximumPoolSize(Integer.valueOf(maximumPoolSize));
		}

		if (StrUtil.isNotBlank(maxLifetime)) {
			dsc.setMaxLifetime(Long.valueOf(maxLifetime));
		}

		if (StrUtil.isNotBlank(minimumIdle)) {
			dsc.setMinimumIdle(Integer.valueOf(minimumIdle));
		}

		String type = config.get("db.type");
		String driverClassName = config.get("db.driverClassName");

		if (StrUtil.isNotBlank(type)) {
			dsc.setType(type);
		}

		if (StrUtil.isNotBlank(driverClassName)) {
			dsc.setDriverClassName(driverClassName);
		}
		return dsc;
	}

}
